#include "w6100_tcp_comm_op.h"

#include "print_rtt.h"
#include "task.h"
#include "timers.h"
#include "w6100.h"
#include "w6100_socket.h"
#include <string.h>

/**
 * @brief
 *
 * @param sn
 * @param recv_buf
 * @param recv_len
 * @param wait_tickets 单位是10ms
 * @return int32_t
 */
int32_t W6100_tcp_recv(uint8_t sn, uint8_t *recv_buf, uint16_t recv_len, uint16_t wait_tickets)
{
  int32_t ret;
  uint16_t received_size;
  uint8_t status, ret_socket;
  if (xSemaphoreTake(w6100_op_xStaticMutex, portMAX_DELAY) != pdTRUE)
  {
    return 0;
  }
  // 1. 获取当前Socket状态（Sn_SR：Socket n状态寄存器）
  getsockopt(sn, SO_STATUS, &status);
  if (status == SOCK_ESTABLISHED)
  {
    do
    {
      getsockopt(sn, SO_RECVBUF, &received_size);
      if (received_size > 0)
      {
        if (received_size > recv_len)
        {
          received_size = recv_len; // DATA_BUF_SIZE means user defined buffer size (array)
        }
        ret = recv(sn, recv_buf, received_size);
        xSemaphoreGive(w6100_op_xStaticMutex);
        return ret;
      }
      wait_tickets--;
      vTaskDelay(pdMS_TO_TICKS(10));
    } while (wait_tickets > 0);
    xSemaphoreGive(w6100_op_xStaticMutex);
    return 0;
  }
  else
  {
    ret_socket = disconnect(sn);
    xSemaphoreGive(w6100_op_xStaticMutex);
    if (ret_socket == SOCK_OK)
    {
      return SOCKERR_SOCKCLOSED;
    }
    else
    {
      return 0;
    }
  }
}
/**
 * @brief
 *
 * @param sn
 * @param send_buf
 * @param send_len
 * @return int32_t
 */
int32_t W6100_tcp_send(uint8_t sn, uint8_t *send_buf, uint16_t send_len)
{
  int32_t ret;
  uint16_t sentsize;
  uint8_t status, ret_socket;
  // 1. 获取当前Socket状态（Sn_SR：Socket n状态寄存器）
  if (xSemaphoreTake(w6100_op_xStaticMutex, portMAX_DELAY) != pdTRUE)
  {
    return 0;
  }
  getsockopt(sn, SO_STATUS, &status);
  if (status == SOCK_ESTABLISHED)
  {
    sentsize = 0;
    // Data sentsize control
    while (send_len != sentsize)
    {
      ret = send(sn, send_buf + sentsize, send_len - sentsize); // Data send process (User's buffer -> Destination through H/W Tx socket buffer)
      if (ret < 0)                                              // Send Error occurred (sent data length < 0)
      {
        close(sn); // socket close
        xSemaphoreGive(w6100_op_xStaticMutex);
        return ret;
      }
      sentsize += ret; // Don't care SOCKERR_BUSY, because it is zero.
    }
    xSemaphoreGive(w6100_op_xStaticMutex);
    return send_len;
  }
  else
  {
    ret_socket = disconnect(sn);
    xSemaphoreGive(w6100_op_xStaticMutex);
    return ret_socket;
    //    if (ret_socket == SOCK_OK)
    //    {
    //      return SOCKERR_SOCKCLOSED;
    //    }
    //		return 0;
  }
}

/**
 * @brief TCP连接管理（非ESTABLISHED状态则关闭重连，成功返回0）
 * @param sn  socket编号
 * @param destip 目标IP地址
 * @param destport 目标端口
 * @param loopback_mode 网络模式（IPv4/IPv6/双栈）
 * @return 0：连接成功，错误码（<0）：失败
 */
int32_t W6100_tcp_connect(uint8_t sn, uint16_t *src_port, uint8_t *destip, uint16_t destport, uint8_t loopback_mode)
{
  int32_t ret, times;
  wiz_IPAddress chip_desIP;
  uint16_t chip_desport;
  uint8_t status, inter, arg_tmp8;
  int8_t tmp;
  if (xSemaphoreTake(w6100_op_xStaticMutex, portMAX_DELAY) != pdTRUE)
  {
    return 0;
  }
  // 1. 获取当前Socket状态（Sn_SR：Socket n状态寄存器）
  getsockopt(sn, SO_STATUS, &status);
  if (status == SOCK_ESTABLISHED)
  {
    // 当前是连接的，所以不用连接。
    getsockopt(sn, SO_DESTIP, &chip_desIP);
    ret = memcmp(&chip_desIP.ip[0], destip, 4);
    if (ret == 0)
    {
      getsockopt(sn, SO_DESTPORT, &chip_desport);
      ret = memcmp(&chip_desport, &destport, 2);
      if (ret == 0)
      {
        *src_port = getSn_PORTR(sn);
#ifdef _LOOPBACK_DEBUG_
        LOG_INFO("%d:Socket already connected, status=%d\r\n", sn, status);
#endif
        xSemaphoreGive(w6100_op_xStaticMutex);
        return 0;
      }
    }
  }

  if (status != SOCK_CLOSED)
  {
#ifdef _LOOPBACK_DEBUG_
    LOG_ERROR("%d:Status not CLOSED(%d), closing socket\r\n", sn, status);
#endif
    close(sn);
    while (1)
    {
      getsockopt(sn, SO_STATUS, &status);
      if (status == SOCK_CLOSED)
      {
#ifdef _LOOPBACK_DEBUG_
        LOG_INFO("%d:Socket closed, status=%d\r\n", sn, status);
#endif
        break;
      }
      if (getSn_IR(sn) & Sn_IR_TIMEOUT)
      {

        setSn_IRCLR(sn, Sn_IR_TIMEOUT);
#ifdef _LOOPBACK_DEBUG_
        LOG_INFO("%d:Close socket timeout, status=%d\r\n", sn, status);
#endif
        break;
      }
    }
  }
  /// 开始建立连接
  switch (loopback_mode)
  {
  case AS_IPV4:
    tmp = socket(sn, Sn_MR_TCP4, src_port, SOCK_IO_NONBLOCK);
    break;
  case AS_IPV6:
    tmp = socket(sn, Sn_MR_TCP6, src_port, SOCK_IO_NONBLOCK);
    break;
  case AS_IPDUAL:
    tmp = socket(sn, Sn_MR_TCPD, src_port, SOCK_IO_NONBLOCK);
    break;
  default:
    break;
  }
  if (tmp != sn)
  { /* reinitialize the socket */
#ifdef _LOOPBACK_DEBUG_
    LOG_INFO("%d : Fail to create socket.\r\n", sn);
#endif
    return SOCKERR_SOCKNUM;
  }
  LOG_INFO("%d:Socket opened[%d]\r\n", sn, getSn_SR(sn));
  // connect

  while (1)
  {
    getsockopt(sn, SO_STATUS, &status);
    times++;
    if (times > 1000)
    {
      xSemaphoreGive(w6100_op_xStaticMutex);
      return SOCKERR_TIMEOUT;
    }
    if (status == SOCK_INIT)
    {
      break;
    }
  }

#ifdef _LOOPBACK_DEBUG_
  if (loopback_mode == AS_IPV4)
    LOG_INFO("%d:Try to connect to the %d.%d.%d.%d, %d\r\n", sn, destip[0], destip[1], destip[2], destip[3], destport);
  else if (loopback_mode == AS_IPV6)
  {
    LOG_INFO("%d:Try to connect to the %04X:%04X", sn, ((uint16_t)destip[0] << 8) | ((uint16_t)destip[1]),
             ((uint16_t)destip[2] << 8) | ((uint16_t)destip[3]));
    LOG_INFO(":%04X:%04X", ((uint16_t)destip[4] << 8) | ((uint16_t)destip[5]),
             ((uint16_t)destip[6] << 8) | ((uint16_t)destip[7]));
    LOG_INFO(":%04X:%04X", ((uint16_t)destip[8] << 8) | ((uint16_t)destip[9]),
             ((uint16_t)destip[10] << 8) | ((uint16_t)destip[11]));
    LOG_INFO(":%04X:%04X,", ((uint16_t)destip[12] << 8) | ((uint16_t)destip[13]),
             ((uint16_t)destip[14] << 8) | ((uint16_t)destip[15]));
    LOG_INFO("%d\r\n", destport);
  }
#endif
  if (loopback_mode == AS_IPV4)
    ret = connect(sn, destip, destport, 4); /* Try to connect to TCP server(Socket, DestIP, DestPort) */
  else if (loopback_mode == AS_IPV6)
    ret = connect(sn, destip, destport, 16); /* Try to connect to TCP server(Socket, DestIP, DestPort) */

#ifdef _LOOPBACK_DEBUG_
  LOG_INFO("SOCK Status: %d\r\n", ret);
#endif
  if (ret == SOCK_OK)
  {
    ctlsocket(sn, CS_GET_INTERRUPT, &inter); // 保护
    if (inter & Sn_IR_CON)                   // Socket n interrupt register mask; TCP CON interrupt = connection with peer is successful
    {
#ifdef _LOOPBACK_DEBUG_
      LOG_INFO("%d:Connected to - %d.%d.%d.%d : %d\r\n", sn, destip[0], destip[1], destip[2], destip[3], destport);
#endif
      arg_tmp8 = Sn_IR_CON;
      ctlsocket(sn, CS_CLR_INTERRUPT, &arg_tmp8); // this interrupt should be write the bit cleared to '1'
    }

    while (1)
    {
      /* code */
      getsockopt(sn, SO_STATUS, &status);
      if (status == SOCK_ESTABLISHED)
      {
        break;
      }
    }
  }
  xSemaphoreGive(w6100_op_xStaticMutex);
  return ret; //	Try to TCP connect to the TCP server (destination)
}
int32_t W6100_tcp_disconnect(uint8_t sn)
{
  uint32_t ret;
  if (xSemaphoreTake(w6100_op_xStaticMutex, portMAX_DELAY) != pdTRUE)
  {
    return 0;
  }
  ret = disconnect(sn);
  if (ret != SOCK_OK)
  {
#ifdef _LOOPBACK_DEBUG_
    LOG_INFO("%d:Socket Close failed--%d\r\n", sn, ret);
#endif
    xSemaphoreGive(w6100_op_xStaticMutex);
    return ret;
  }
#ifdef _LOOPBACK_DEBUG_
  LOG_INFO("%d:Socket Closed\r\n", sn);
#endif
  xSemaphoreGive(w6100_op_xStaticMutex);
  return SOCK_OK;
}
